using System.Collections;
using System.Collections.Generic;
using Pathfinding;
using UnityEngine;

namespace Enemy
{
    // 有限状态机类
    public class FSM : MonoBehaviour
    {
        private IState currentState;        // 当前状态接口
        protected Dictionary<StateType, IState> states = new Dictionary<StateType, IState>();  // 状态字典，存储各种状态

        public Parameter parameter;     // 状态机参数

        protected virtual void Awake() { }

        protected virtual void OnEnable()
        {
            parameter.animator = transform.GetComponent<Animator>();  // 获取角色上的动画控制器组件
            parameter.seeker = GetComponent<Seeker>();
            parameter.rb = GetComponent<Rigidbody2D>();

            TransitionState(StateType.Idle);    // 初始状态为Idle
            currentState.OnEnter();
        }

        void Update()
        {
            parameter.animatorStateInfo = parameter.animator.GetCurrentAnimatorStateInfo(0);// 获取当前动画状态信息
            GetPlayerTransform();

            currentState.OnUpdate();    // 每帧更新当前状态
        }


        void FixedUpdate()
        {
            currentState.OnFixedUpdate();
        }

        // 状态转换方法
        public void TransitionState(StateType type)
        {
            if (currentState != null)
                currentState.OnExit();  // 如果当前状态不为空，调用退出方法

            currentState = states[type];    // 更新当前状态为指定类型的状态
            currentState.OnEnter();         // 调用新状态的进入方法
        }

        // 翻转角色朝向方法，使其朝向目标
        public void FlipTo(Transform target)
        {
            if (target != null)
            {
                if (transform.position.x > target.position.x)
                {
                    transform.localScale = new Vector3(-Mathf.Abs(transform.localScale.x), transform.localScale.y, transform.localScale.z);
                }
                else if (transform.position.x < target.position.x)
                {
                    transform.localScale = new Vector3(Mathf.Abs(transform.localScale.x), transform.localScale.y, transform.localScale.z);
                }
            }
        }

        // 查找玩家的方法
        public void GetPlayerTransform()
        {
            // 使用Physics2D.OverlapCircleAll获取位于指定距离内的所有Collider2D数组
            Collider2D[] chaseColliders = Physics2D.OverlapCircleAll(transform.position, parameter.chaseDistance, parameter.targetLayer);

            // 如果找到了玩家
            if (chaseColliders.Length > 0)
            {
                // 将第一个找到的玩家设为追踪目标
                parameter.target = chaseColliders[0].transform;
                // 计算与目标的距离
                // distance = Vector2.Distance(parameter.target.position, transform.position);
            }
            else
            {
                // 如果没有找到玩家，则目标置空
                parameter.target = null;
            }
        }

        #region A*寻路方法
        //每隔一段时间重新计算路径路径
        public void StartPath(Transform target)
        {
            if (Time.time > parameter.lastRepath + parameter.repathRate && parameter.seeker.IsDone())
            {
                parameter.lastRepath = Time.time;

                parameter.seeker.StartPath(transform.position, target.position, OnPathComplete);
            }
        }

        //寻路移动
        public void Move()
        {
            // 还没有路径可以跟随，所以不执行任何操作
            if (parameter.path == null) return;

            // 循环检查是否已经接近当前路径点，可以切换到下一个点
            // 使用循环是因为许多路径点可能非常接近，可能在同一帧内到达多个路径点
            parameter.reachedEndOfPath = false;
            // 当前路径点到代理的距离
            float distanceToWaypoint;
            while (true)
            {
                // 如果希望最大化性能，可以检查平方距离而不是实际距离，避免使用平方根计算，但这超出了本教程的范围
                distanceToWaypoint = Vector3.Distance(transform.position, parameter.path.vectorPath[parameter.currentWaypoint]);
                if (distanceToWaypoint < parameter.nextWaypointDistance)
                {
                    // 检查是否还有下一个路径点，或者是否已经到达路径的末尾
                    if (parameter.currentWaypoint + 1 < parameter.path.vectorPath.Count)
                    {
                        parameter.currentWaypoint++;
                    }
                    else
                    {
                        // 设置一个状态变量，表示代理已经到达路径的末尾
                        // 如果你的游戏需要，可以使用这个变量来触发一些特殊代码
                        parameter.reachedEndOfPath = true;
                        break;
                    }
                }
                else
                {
                    break;
                }
            }
            // 在接近路径末尾时平滑减速
            // 这个值会在代理接近路径的最后一个路径点时，从 1 平滑过渡到 0
            var speedFactor = parameter.reachedEndOfPath ? Mathf.Sqrt(distanceToWaypoint / parameter.nextWaypointDistance) : 1f;
            // 到下一个路径点的方向
            // 归一化，使其长度为1个世界单位
            Vector3 dir = (parameter.path.vectorPath[parameter.currentWaypoint] - transform.position).normalized;
            // 将方向乘以我们期望的速度，得到速度向量
            Vector3 velocity = dir * parameter.currentSpeed * speedFactor;
            // 移动到目标点
            // transform.position += velocity * Time.deltaTime;
            parameter.rb.velocity = velocity;
        }

        //路径计算完成时回调方法
        public void OnPathComplete(Path p)
        {
            Debug.Log("计算出一条路径。是否出现错误？" + p.error);

            // 路径池。为了避免不必要的内存分配，路径使用引用计数。
            // 调用 Claim 方法将引用计数加一，调用 Release 方法将其减一，
            // 当引用计数为零时，路径将被放入池中，其他脚本可以重用该路径。
            // ABPath.Construct 和 Seeker.StartPath 方法会尽可能从池中获取路径。详见路径池文档页面。
            p.Claim(this);
            if (!p.error)
            {
                if (parameter.path != null) parameter.path.Release(this);
                parameter.path = p;
                // 重置路径点计数器，以便开始移动到路径的第一个点
                parameter.currentWaypoint = 0;
            }
            else
            {
                p.Release(this);
            }
        }
        #endregion


        // 触发器进入事件，检测到玩家时设置目标为玩家
        private void OnTriggerEnter2D(Collider2D other)
        {
            if (other.CompareTag("Player"))
            {
                parameter.target = other.transform;
            }
        }

        // 触发器离开事件，玩家离开时清空目标
        private void OnTriggerExit2D(Collider2D other)
        {
            if (other.CompareTag("Player"))
            {
                parameter.target = null;
            }
        }

        // 开始路径刷新计时
        public void RefreshTiming()
        {
            StartCoroutine(nameof(DodgeOnCooldownCoroutine));
        }

        public IEnumerator DodgeOnCooldownCoroutine()
        {
            parameter.isPathRefresh = false;
            yield return new WaitForSeconds(0.5f);
            parameter.isPathRefresh = true;
        }

        // 在Scene视图中绘制攻击范围的辅助图形
        private void OnDrawGizmos()
        {
            //攻击范围
            Gizmos.DrawWireSphere(parameter.attackPoint.position, parameter.attackArea);

            //追击范围
            Gizmos.color = Color.yellow;
            Gizmos.DrawWireSphere(transform.position, parameter.chaseDistance);
        }
    }

}
